<script setup lang="ts">
  import { ref, computed } from 'vue'
  import { CommonUtil } from '@xtail/core'
  import { AnimateExecTimeConst, AnimateClearTimeConst } from '../../common'
  import { VisibilityDetailHandlerLimitExcludeType, SVisibilityDetail } from '../SVisibilityDetail'
  import { AnimateCssPropsType } from './types'
  import { AnimateCssConst, AnimateCssSpeedConst } from './consts'
  import { ElVisibilityAnimateCssExecTimeMap } from './maps'

  defineOptions({
    name: 'SAnimateCss'
  })

  const props = withDefaults(defineProps<AnimateCssPropsType>(), {
    animate: AnimateCssConst.zoomIn,
    speed: AnimateCssSpeedConst.faster,
    execTime: AnimateExecTimeConst.MOUNTED,
    clearTime: [
      AnimateClearTimeConst.BEFORE_SHOW,
      AnimateClearTimeConst.SHOWED,
      AnimateClearTimeConst.BEFORE_HIDE,
      AnimateClearTimeConst.HIDDEN
    ] as any,
    beforeShow: () => {},
    showed: () => {},
    beforeHide: () => {},
    hidden: () => {}
  })

  defineExpose<{
    getAnimateClass: () => string
  }>({
    getAnimateClass: () => animateClass.value
  })

  /**
   * 同一个动画执行时机配置下，组件在容器中的滚动过程中，下面的四个处理函数都有可能执行。
   * 因此，在与动画执行时机配置相对应的处理函数中添加执行动画，在其他处理函数中可以选择是否对动画进
   * 行清除。
   * 在每个处理函数中，只有与处理函数相对应的动画执行时机配置下，才进行动画的添加和执行。
   */
  const animateClass = ref<string>('')
  const clearAnimate = () => (animateClass.value = '')
  // 是否在组件完全显示之前清除动画
  const isBeforeShowClearAnimate = () =>
    animateClass.value !== '' && props.clearTime.includes(AnimateClearTimeConst.BEFORE_SHOW)
  const isShowedClearAnimate = () =>
    animateClass.value !== '' && props.clearTime.includes(AnimateClearTimeConst.SHOWED)
  const isBeforeHideClearAnimate = () =>
    animateClass.value !== '' && props.clearTime.includes(AnimateClearTimeConst.BEFORE_HIDE)
  const isHiddenClearAnimate = () =>
    animateClass.value !== '' && props.clearTime.includes(AnimateClearTimeConst.HIDDEN)
  const beforeShow = () => {
    if (props.execTime !== AnimateExecTimeConst.BEFORE_SHOW) {
      CommonUtil.isAnyEq(props.execTime, [
        // 当前动画的执行时机为组件完全显示后，且当前组件处于网球显示之前的状态，同时满足数组中
        // 第三个元素指定的条件（即返回 true），则执行动画的清除
        [AnimateExecTimeConst.SHOWED, clearAnimate, isBeforeShowClearAnimate],
        [AnimateExecTimeConst.BEFORE_HIDE, clearAnimate, isBeforeShowClearAnimate]
      ])
      return
    }
    animateClass.value = props.animate
    props.beforeShow()
  }
  const showed = () => {
    if (props.execTime !== AnimateExecTimeConst.SHOWED) {
      CommonUtil.isAnyEq(props.execTime, [
        [AnimateExecTimeConst.BEFORE_SHOW, clearAnimate, isShowedClearAnimate],
        [AnimateExecTimeConst.BEFORE_HIDE, clearAnimate, isShowedClearAnimate]
      ])
      return
    }
    animateClass.value = props.animate
    props.showed()
  }
  const beforeHide = () => {
    if (props.execTime !== AnimateExecTimeConst.BEFORE_HIDE) {
      CommonUtil.isAnyEq(props.execTime, [
        [AnimateExecTimeConst.BEFORE_SHOW, clearAnimate, isBeforeHideClearAnimate],
        [AnimateExecTimeConst.SHOWED, clearAnimate, isBeforeHideClearAnimate]
      ])
      return
    }
    animateClass.value = props.animate
    props.beforeHide()
  }
  const hidden = () => {
    CommonUtil.isAnyEq(props.execTime, [
      [AnimateExecTimeConst.BEFORE_SHOW, clearAnimate, isHiddenClearAnimate],
      [AnimateExecTimeConst.SHOWED, clearAnimate, isHiddenClearAnimate],
      [AnimateExecTimeConst.BEFORE_HIDE, clearAnimate, isHiddenClearAnimate]
    ])
    props.hidden()
  }

  const visibilityDetailHandlerLimitExclude: VisibilityDetailHandlerLimitExcludeType = visibility =>
    Reflect.get(ElVisibilityAnimateCssExecTimeMap, visibility) === props.execTime &&
    animateClass.value !== ''

  const classes = computed(() => [
    animateClass.value,
    props.execTime === AnimateExecTimeConst.MOUNTED ? props.animate : '',
    props.delay ?? '',
    props.speed ?? '',
    props.repeat ?? '',
    props.className ?? ''
  ])
</script>

<template>
  <s-visibility-detail
    class="s-animate-css animate__animated"
    :class="classes"
    :style="style"
    v-bind="{
      beforeShowLimitExclude: visibilityDetailHandlerLimitExclude,
      showedLimitExclude: visibilityDetailHandlerLimitExclude,
      beforeHideLimitExclude: visibilityDetailHandlerLimitExclude,
      hiddenLimitExclude: visibilityDetailHandlerLimitExclude,
      beforeShowNotScrollLimitExclude: visibilityDetailHandlerLimitExclude,
      showedNotScrollLimitExclude: visibilityDetailHandlerLimitExclude,
      hiddenNotScrollLimitExclude: visibilityDetailHandlerLimitExclude,
      ...(visibilityDetailProps ?? {}),
      beforeShow,
      showed,
      beforeHide,
      hidden,
      beforeShowNotScroll: beforeShow,
      showedNotScroll: showed,
      hiddenNotScroll: hidden
    }"
  >
    <slot />
  </s-visibility-detail>
</template>

<style lang="scss">
  @use './style' as *;
</style>
